/**
 * 
 */
package programs;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import bitmap.BMPage;
import bitmap.BitMapFile;
import bitmap.BitMapHeaderPage;
import btree.*;
import bufmgr.BufMgrException;
import bufmgr.BufferPoolExceededException;
import bufmgr.HashEntryNotFoundException;
import bufmgr.HashOperationException;
import bufmgr.InvalidFrameNumberException;
import bufmgr.PageNotReadException;
import bufmgr.PagePinnedException;
import bufmgr.PageUnpinnedException;
import bufmgr.ReplacerException;
import columnar.*;
import diskmgr.PCounter;
import global.*;
import heap.*;
import index.ColumnIndexScan;
import index.IndexException;
import index.IndexScan;
import index.UnknownIndexTypeException;
import iterator.*;

/**
 * @author usr
 * 
 */
public class query {
	private static short REC_LEN_STRING = 160;
	private static short REC_LEN_INT = 32;

	private static Columnarfile cf = null;
	private static String columnDBName = "";
	private static String columnarFileName = "";
	private static String[] targetColumnNames = null;
	private static String[] valueConstraint = null;

	public static void main(String[] args) throws Exception {

		if (args.length != 6) {
			System.out
					.println("The command line invocation must be as follows: \n\t"
							+ "query COLUMNDBNAME COLUMNARFILENAME [TargetColumnNames] "
							+ "ValueConstraint NumBuf AcessType");
			Runtime.getRuntime().exit(1);
		}

		columnDBName = "";
		columnarFileName = "";
		targetColumnNames = null;
		valueConstraint = null;
		String numBuf = "";
		int accessType = 0;

		columnDBName = args[0];
		columnarFileName = args[1];
		targetColumnNames = args[2].replace('[', ' ').replace(']', ' ').trim()
				.split(",");
		valueConstraint = args[3].replace('[', ' ').replace(']', ' ').trim()
				.split(",");
		numBuf = args[4];

		if (args[5].equals("FILESCAN"))
			accessType = QueryType.FileScan;
		else if (args[5].equals("COLUMNSCAN"))
			accessType = QueryType.ColumnScan;
		else if (args[5].equals("BTREE"))
			accessType = QueryType.BTree;
		else if (args[5].equals("BITMAP"))
			accessType = QueryType.BitMap;
		else {
			System.out
					.println("Only AccessTypes allowed:FILESCAN, COLUMNSCAN, BTREE or BITMAP");
			Runtime.getRuntime().exit(1);
		}

		// read an existing DB into BM
		global.SystemDefs sysdef = new global.SystemDefs(columnDBName, 0,
				Integer.parseInt(numBuf), "Clock");

		// try read a columnarfile form DB
		try {
			cf = new Columnarfile(columnarFileName);
		} catch (Exception ex) {
			System.out
					.println("Unable to read a columnar file from DB\n\t check if it has been created first\n");
			Runtime.getRuntime().exit(1);
		}

		// execute query
		switch (accessType) {
		case QueryType.FileScan:
			DoFileScan();
                        sysdef.JavabaseBM.flushAllPages();
			break;
		case QueryType.ColumnScan:
			DoColumnScan();
                        sysdef.JavabaseBM.flushAllPages();
			break;
		case QueryType.BTree:
			DoBTree();
                        sysdef.JavabaseBM.flushAllPages();
			break;
		case QueryType.BitMap:
			DoBitMap();
			break;
		default:
			System.out
					.println("Unable to identify ACCESSTYPE parameter, please check your input\n");
			Runtime.getRuntime().exit(1);
		}

		
		// print number of processed pages
		PCounter.print();

	}

	static private void DoFileScan() throws Exception {
		AttrType[] types = new AttrType[targetColumnNames.length];
		short[] sizes = new short[targetColumnNames.length];

		FldSpec[] projection = new FldSpec[targetColumnNames.length];
		for (int i = 0; i < targetColumnNames.length; i++) {
			projection[i] = new FldSpec(new RelSpec(RelSpec.outer), cf
					.getColumnOffset(targetColumnNames[i]));
			types[i] = cf.getColumnType(targetColumnNames[i]);
			if (types[i].attrType == AttrType.attrInteger)
				sizes[i] = REC_LEN_INT;
			else
				sizes[i] = REC_LEN_STRING;
		}

		// expr select expression
		CondExpr[] expr = new CondExpr[2];
		expr[0] = new CondExpr();
		expr[0].op = new AttrOperator(Integer.valueOf(valueConstraint[1]));
		expr[0].type1 = new AttrType(AttrType.attrSymbol);
		expr[0].type2 = cf.getColumnType(valueConstraint[0]);
		expr[0].operand1.symbol = new FldSpec(new RelSpec(RelSpec.outer), cf
				.getColumnOffset(valueConstraint[0]));

		if (expr[0].type2.attrType == AttrType.attrInteger)
			expr[0].operand2.integer = Integer.parseInt(valueConstraint[2]);
		else
			expr[0].operand2.string = valueConstraint[2];

		expr[1] = null;

		ColumnarFileScan cfs = null;
		try {
			cfs = new ColumnarFileScan(cf.columnarFileName(), types, sizes,
					(short) targetColumnNames.length,
					(short) targetColumnNames.length, projection, expr);
		} catch (Exception e) {
			System.err.println("" + e);
		}

		Sort sort = cf.startSortDelMethod();
		Tuple t = null;
		try {
			if ((t = sort.get_next()) == null) {
				Tuple tuple = new Tuple();
				while ((tuple = cfs.get_next()) != null) {
					tuple.print(types);
				}
			} else {
				List<Integer> positions = new ArrayList<Integer>();
				do {
					positions.add(t.getIntFld(1));
				} while ((t = sort.get_next()) != null);
				Tuple tuple = null;

				while ((tuple = cfs.get_next()) != null) {
					Integer val = cfs.GetCurrentPostion();
					if (positions.contains(val))
						break;
					tuple.print(types);
				}
			}
		} catch (Exception ex) {
		} finally {
			sort.close();
			cfs.close();
		}

	}

	static private void DoColumnScan() throws InvalidSlotNumberException,
			HFException, HFDiskMgrException, HFBufMgrException, Exception {
		AttrType[] types = { cf.getColumnType(valueConstraint[0]) };
		short[] sizes = { REC_LEN_STRING };

		FldSpec[] projection = new FldSpec[1];
		projection[0] = new FldSpec(new RelSpec(RelSpec.outer), 1);

		// expr select expression
		CondExpr[] expr = new CondExpr[2];
		expr[0] = new CondExpr();
		expr[0].op = new AttrOperator(Integer.valueOf(valueConstraint[1]));
		expr[0].type1 = new AttrType(AttrType.attrSymbol);
		expr[0].type2 = types[0];
		expr[0].operand1.symbol = new FldSpec(new RelSpec(RelSpec.outer), 1);

		if (types[0].attrType == AttrType.attrInteger)
			expr[0].operand2.integer = Integer.parseInt(valueConstraint[2]);
		else
			expr[0].operand2.string = valueConstraint[2];

		expr[1] = null;

		FileScan fs = null;
		try {
			fs = new FileScan(cf.getColumnFileName(valueConstraint[0]), types,
					sizes, (short) 1, (short) 1, projection, expr);
		} catch (Exception e) {
			System.err.println("" + e);
		}

		Sort sort = cf.startSortDelMethod();
		Tuple t = null;
		try {
			if ((t = sort.get_next()) == null) {
				Tuple tuple = new Tuple();
				while ((tuple = fs.get_next()) != null) {
					tuple.print(types);
				}
			} else {

				List<Integer> positions = new ArrayList<Integer>();
				do {
					positions.add(t.getIntFld(1));
				} while ((t = sort.get_next()) != null);
				Tuple tuple = null;

				while ((tuple = fs.get_next()) != null) {
					Integer val = fs.getCurrentRecordPosition();
					if (positions.contains(val))
						break;
					tuple.print(types);
				}
			}
		} catch (Exception ex) {
		} finally {
			sort.close();
			fs.close();
		}
	}

	static private void DoBTree() throws JoinsException, PageNotReadException,
			TupleUtilsException, PredEvalException, SortException,
			LowMemException, UnknowAttrType, UnknownKeyTypeException, Exception {
		AttrType[] types = { cf.getColumnType(valueConstraint[0]) };
		short[] sizes = { REC_LEN_STRING };

		FldSpec[] projection = new FldSpec[1];
		projection[0] = new FldSpec(new RelSpec(RelSpec.outer), 1);

		// expr select expression
		CondExpr[] expr = new CondExpr[2];
		expr[0] = new CondExpr();
		expr[0].op = new AttrOperator(Integer.valueOf(valueConstraint[1]));
		expr[0].type1 = new AttrType(AttrType.attrSymbol);
		expr[0].type2 = types[0];
		expr[0].operand1.symbol = new FldSpec(new RelSpec(RelSpec.outer), 1);

		if (types[0].attrType == AttrType.attrInteger)
			expr[0].operand2.integer = Integer.parseInt(valueConstraint[2]);
		else
			expr[0].operand2.string = valueConstraint[2];

		expr[1] = null;

		String fileName = cf.getColumnFileName(valueConstraint[0]);

		Iterator it = new IndexScan(new IndexType(IndexType.B_Index), fileName,
				fileName + ".btree", types, sizes, 1, 1, projection, expr, 1,
				true);

		// Iterator it = new ColumnIndexScan(new IndexType(IndexType.B_Index),
		// fileName,
		// fileName + ".btree",
		// types[0],
		// size, expr, false);

		Tuple tuple = null;
		while ((tuple = it.get_next()) != null) {
			tuple.print(types);
		}

		it.close();
	}

	static private void DoBitMap() {
		BitMapFile bmf;
		BitMapHeaderPage header;
		try {
			bmf = new BitMapFile(cf.getColumnFileName(valueConstraint[0]) + "."
					+ "bitmap" + "." + valueConstraint[2]);
			header = bmf.getHeaderPage();
			if (header.get_rootId().pid == -1) // INVALID_PAGE
			{
				System.out.println("The BitMap is Empty!!!");
				return;
			}
			printTuple(targetColumnNames, header.get_rootId(), 0);
		//	PCounter.print();
		} catch (Exception e) {
		//	PCounter.print();
			// System.out.println("The bitmap does not exist, please use program index to create bitmap first.");
			Runtime.getRuntime().exit(1);
		}
	}

	
	
	static private void printTuple(String[] targetColumnNames,PageId currentPageId, int position) 
                        throws  ReplacerException,PageUnpinnedException, HashEntryNotFoundException,
                                InvalidFrameNumberException, IOException, HashOperationException,
                                PageNotReadException, BufferPoolExceededException,
                                PagePinnedException, BufMgrException, InvalidTupleSizeException,
                                InvalidTypeException, FieldNumberOutOfBoundException, ConstructPageException, Exception
        {
            BMPage tempPage = new BMPage();
            Tuple tuple = null;
            AttrType[] type = cf.getAttrTypes();
            int ColumnNo;
            if(currentPageId.pid == -1)
                return;
            SystemDefs.JavabaseBM.pinPage(currentPageId, tempPage, false/* Rdisk */); // get the page from the DB
            byte[] tempArray = null;
            tempArray = tempPage.getBMpageArray(); // get the data array
            for (int i = 0; i < tempArray.length; i++) {
                if (tempArray[i] == 0) {
                    position += 8;
		} else {
                    for (int j = 0; j < 8; j++) {
			if (((tempArray[i] >>> j) & 1) == 1) {
                            for (int k = 0; k < targetColumnNames.length; k++) {
                                ColumnNo = cf.getColumnOffset(targetColumnNames[k]) - 1;
				tuple = cf.ColumnArray()[ColumnNo].getTupleForGivenPosition(position);
				Columnarfile.SetOneColumnTupleHeader(tuple,type[ColumnNo]);
				if (type[ColumnNo].attrType == AttrType.attrInteger) {
                                    System.out.print(tuple.getIntFld(1));
				} else if (type[ColumnNo].attrType == AttrType.attrString) {
                                    System.out.print(tuple.getStrFld(1));
				}
				System.out.print(" ");
                            }
                            System.out.print("\n");
			}
			position++;
                    }
		}
            }
            System.out.print("\n");
            if (tempPage.getNextPage().pid != 0) {
		SystemDefs.JavabaseBM.unpinPage(currentPageId, false/* not dirty */);
		printTuple(targetColumnNames, tempPage.getNextPage(), position); // continue to print the next page
            } else
		SystemDefs.JavabaseBM.unpinPage(currentPageId, false/* not dirty */);
	}
}
